Micron Document
Gemini Proxy


bbs.geminispace.org bbs.geminispace.org/u/stack/46801
Comment by 🚀 stack


Yes, it's not complicated but I invariably get it wrong the first time for some reason.

Aug 08 · 6 days ago


20 Later Comments ↓

Also, I have yet to encounter a situation where I was happy with using const. I can think of several where I had to go through code removing constness.


The easiest way to deal with 'const' and pointers is to read them right to left.

int main() {
int x = 1;
int y = 2;

// p is a pointer to an int that is const
const int *p = &x;
*p = 3; // fails
p = &y;

// q is a const pointer of an int
int *const q = &x;
*q = 3;
q = &y; // fails
return 0;
}
@stack : "I can think of several where I had to go through code removing constness."

The only good reason I can think of for doing this, is that you have to incorporate a library supplied by somebody who didn't understand how const works.

The typical problem is that you have a library function that wrongly takes a non-const parameter, and you have to call it from a function, where the parameters are correctly const.

If you remove the const from your own function, then you'll probably find that some other compilation fails, because something else isn't const that should be. And so it goes on, until you've stripped all the const declarations and destroyed a major code quality feature.


The only good reason I can think of for doing this, is that you have to incorporate a library supplied by somebody who didn't understand how const works.

I would argue (and have argued)
that the 'free' C standard library

For those wondering why you would want to free a pointer to a const object, think of a shared read-only string—there may be any number of references to it, and it can be reference-counted so you can free it when it's no longer used by anything. But because of this flaw in the standard library, you have to strip the constness:

// const char *s;
free((/* non-const */ char *)s);
(The comment inside the cast explains the reason for the cast.)


@Christopher const means that you are not going to modify it. free() *does* modify it: It changes it from a defined value to an undefined value. So it very much mutates it and must not be const. That free isn't const is not an oversight at all – this is extremely intentional.


@js, while I totally understand what you are saying, I would argue that free renders a pointer to it completely invalid and dangling and discussion of whatever it is pointing to being const or not -- entirely moot


I would argue that having free() defined as it is makes it easier for the compiler to prevent us from making mistakes like this:

const char *message = get_error_text(...);
...
free (message)
I've defined get_error_text() to return a const * because I know that the string it returns is not dynamically allocated, so should not be free'd.

While I understand the point that @Christopher makes, on balance I think it's better that free() is defined as it is.


I avoid dynamic allocation like the plague, and it is often possible to do so. In my projects anyway, which tend to be very contained.


@stack : "I avoid dynamic allocation like the plague"

I'd agree that it's wise to avoid it where possible, both from code quality and efficiency perspectives.

But how would you implement, say, a variable-length list of text strings of variable size, without some measure of dynamic allocation? One strategy is to make everything over-sized and over-long, and modern computers have enough memory that this is often practicable. But I started programming in the 70s, and that kind of thing sticks in my throat.

In any case, the sad fact is that if you're working with data structures like trees and graphs, it may be impossible without dynamic allocation.

But prove me wrong :)


I've been working a lot with strings, and there are a couple of methods I use a lot.

The best way is elimination. For compilers, parsers and constrained user input (like in Spellbinding), I hash strings with FNV1a and throw them away. It is _way_ easier dealing with fixed-size tokens.

For situation where I need to aggregate variable-sized text, I try to use log strucures, preallocated. I then flush them at session end, or in more complicated situations, build specialized compactors and garbage collectors, some even fixing up pointers in code. By then I am far from C though.


I've defined get_error_text() to return a const * because I know that the string it returns is not dynamically allocated, so should not be free'd.

The problem is that const doesn't mean a pointer is not dynamically allocated; there actually is no type modifier that means that. And the problem can still happen if a function returns a non-const pointer to statically allocated memory. In either case it's a user problem (i.e., a user of the function). For example, 'gmtime' returns 'struct tm *'. It's not dynamically allocated either and shouldn't be freed by the caller.


In my personal experience, the few times I got tangled up in situations in which it was difficult to figure out when it is safe to free objects, it was entirely due to not thinking enough during the design phase.

Const is a pretty crude tool, and even sophisticated GC or borrow systems are no substitute for thinking (and frankly, just get in the way)


@Christopher : "The problem is that const doesn't mean a pointer is not dynamically allocated;..."

It does if I say it does :) That is, in my programming practice, that's what it means. If I write a function that returns a non-const pointer, the caller knows to take responsibility for it. gmtime() is an unusual case; I assume it was defined as it is before it's archaic. In my own practice, I wrap functions like this in my own stubs, so I can follow consistent memory management semantics.


@stack : Sure, none of this is a subsitute for thinking. It's no substitute for proper documentation, either.

But some of the software I maintain is more than twenty years old. At my age, when I can't always remember what I had for breakfast, I need to use whatever tools a programming language offers, to reduce the memory burden.

Using the const-correctness features of C and C++, methodically and consistently, has saved me from stupid, dangerous errors more often than I can remember.


@lars_the_bear--I am there too, and understand the sentiment... Just can't say const has ever helped me, no doubt due to being a dufus.


@stack : "...no doubt due to being a dufus."

More likely, perhaps, because you didn't start your programmic life with C/C++. I didn't get it at first, then I had an "aha!" moment when I realized why it mattered.

I see the same thing with C++ programmers, who don't understand why Java's compile-time exception checking is such a powerful tool.

No programming language needs any of these features, but I take the view that anything that allows an error to be caught before even running the program has to be a good idea, even if it's inconvenient.


Ok, I will give it a shot next time.


gmtime() is an unusual case

It's not so unusual. I can think of other standard library functions, such as fopen(), that return a pointer to a non-const object which should not be freed by the application. Since the pointer is not const-qualified, your application _could_ try to free() it, but Bad Things are likely to happen. Also see genenv(), strerror(), strchr(), strstr(), strtok(), and setlocale(), among others.


@Christopher : Fair point, but fopen() returns a pointer that becomes the responsibility of the caller. You can't call free() on it, but you can call fclose().

strstr(), strtok(), etc., don't really count, because they don't allocate anything.

It's always bugged me that strerror() returns a non-const. I suppose it can be forgiven, because (I guess) it pre-dates proper const semantics. GNU libc has strerrordesc_np() which does return a const pointer.

But, broadly, I agree -- there are functions in the standard library that don't follow modern practices for handling const values, presumably for historical reasons.


strstr(), strtok(), etc., don't really count, because they don't allocate anything.

You're right, and that's exactly the point I'm making. They return pointers to non-const, non-dynamically allocated memory. If you assume that a pointer to non-const can be freed, you'll be hosed if you try to free one of those pointers. If you have any function that returns a pointer (either const or non-const) you need to check its documentation to know if you need to free it yourself. If you get it wrong, you'll either have a memory leak or a corrupted heap.

Also consider this code:

void foo()
{
const int x = 5;
// ...
}
When this function returns, the automatic variable x is deallocated despite it being const. Does the value of x change when it's deallocated? Nope, the object itself just doesn't exist anymore. Same goes for free()ing an object that was malloc()ed.

TL;DR: Non-const doesn't mean dynamically allocated, and const doesn't mean non-dynamically allocated. Using const to mean non-dynamically allocated is an unusual convention and is definitely not idiomatic C.

Edit: here's a good read about const and why the Linux kfree() function takes a const pointer:

https://yarchive.net/comp/const.html

Another edit (sorry): constness and lifetime are distinct properties of an object. As in my example code above, a const object can be deallocated (its lifetime ends). So it would make sense for free() to take a const pointer, as free() doesn't modify the object—it only affects its lifetime.


Original Post

how do i return struct from a function or something like this in C? sorry for bad english.

you're on bbs.geminispace.org/u/stack/46801